﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace XsdParser
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Xml;
    using System.Xml.XPath;
    using System.Xml.Schema;
    using System.Text;

    public class XsdToHtmlParser
    {
        XmlDocument doc;
        StringBuilder RenderedHtml = new StringBuilder();

        private const string typeKeyWord = "type";
        private const string complexTypeKeyWord = "complexType";
        private const string nameKeyWord = "name";
        private const string attributeKeyWord = "attribute";

        private XmlNamespaceManager manager;
        public XsdToHtmlParser(XmlDocument doc)
        {
            this.doc = doc;
            manager = new XmlNamespaceManager(doc.NameTable);
            manager.AddNamespace("xs", "http://www.w3.org/2001/XMLSchema");
        }
        private XmlNode FindRootElement()
        {
            //假设<xs:sechema>下只有一个<xs:element>
            return doc.DocumentElement.SelectSingleNode("xs:element[@name]", manager);
        }
        public string GetHtml()
        {
            this.Process(this.FindRootElement());
            this.RenderedHtml.Append("</table>");

            return this.RenderedHtml.ToString();
        }
        private void Process(XmlNode element)
        {
            if (element == null)
                return;
            #region 头
            XmlNode typeAttribute = element.Attributes["type"];
            XmlNode typeDefiningElement;
            if (typeAttribute != null)//element's type是单独define的
                typeDefiningElement = doc.SelectSingleNode(string.Format("./xs:schema/xs:complexType[@name=\"{0}\"]", typeAttribute.Value), manager);
            else//element没有"type" attribute,而是嵌在<xs:element></xs:element>里
                typeDefiningElement = element.SelectSingleNode("xs:complexType", manager);

            //header
            RenderedHtml.Append("<table border=\"1\"><tr>");
            XmlNodeList attributes = typeDefiningElement.SelectNodes(string.Format("xs:{0}", attributeKeyWord), manager);

            string attributeName;
            foreach (XmlNode n in attributes)
            {
                attributeName = n.Attributes[nameKeyWord, string.Empty].Value;
                RenderedHtml.Append(string.Format("<td>{0}</td>", attributeName));
            }
            RenderedHtml.Append("</tr>");

            //a data row
            RenderedHtml.Append("<tr>");
            string attributeType;
            foreach (XmlNode n in attributes)
            {
                attributeType = n.Attributes[typeKeyWord, string.Empty].Value;
                switch (attributeType)
                {
                    case "xs:string":
                        RenderedHtml.Append("<td><input type=\"text\"/></td>");
                        break;
                    case "xs:int":
                        RenderedHtml.Append("<td><input type=\"text\"/></td>");
                        break;
                    case "xs:double":
                        RenderedHtml.Append("<td><input type=\"text\"/></td>");
                        break;
                    case "xs:boolean":
                        RenderedHtml.Append("<td><input type=\"radio\"/></td>");
                        break;
                }
            }
            RenderedHtml.Append("</tr>");
            #endregion

            #region 转
            //假设<xs:complexType>下只可能出现<xs:sequence>
            XmlNode childElement = typeDefiningElement.SelectSingleNode("xs:sequence/xs:element", manager);
            if (childElement != null)//递归终止条件
            {
                RenderedHtml.Append(string.Format("<tr><td colspan=\"{0}\">", attributes.Count));

                this.Process(childElement);//递归
            }
            else
            {
                //table的第二行数据
                RenderedHtml.Append("<tr>");
                foreach (XmlNode n in attributes)
                {
                    attributeType = n.Attributes[typeKeyWord, string.Empty].Value;
                    switch (attributeType)
                    {
                        case "xs:string":
                            RenderedHtml.Append("<td><input type=\"text\"/></td>");
                            break;
                        case "xs:int":
                            RenderedHtml.Append("<td><input type=\"text\"/></td>");
                            break;
                        case "xs:double":
                            RenderedHtml.Append("<td><input type=\"text\"/></td>");
                            break;
                        case "xs:boolean":
                            RenderedHtml.Append("<td><input type=\"radio\"/></td>");
                            break;
                    }
                }
                RenderedHtml.Append("</tr>");

                return;//递归终止
            }
            #endregion

            #region 尾
            RenderedHtml.Append("</table></td></tr>");

            //table的第二行数据
            RenderedHtml.Append("<tr>");
            foreach (XmlNode n in attributes)
            {
                attributeType = n.Attributes[typeKeyWord, string.Empty].Value;
                switch (attributeType)
                {
                    case "xs:string":
                        RenderedHtml.Append("<td><input type=\"text\"/></td>");
                        break;
                    case "xs:int":
                        RenderedHtml.Append("<td><input type=\"text\"/></td>");
                        break;
                    case "xs:double":
                        RenderedHtml.Append("<td><input type=\"text\"/></td>");
                        break;
                    case "xs:boolean":
                        RenderedHtml.Append("<td><input type=\"radio\"/></td>");
                        break;
                }
            }
            RenderedHtml.Append("</tr>");

            #endregion
        }
    }

}
